"""
音频数据处理器

处理IoT设备的音频数据，进行语音识别和意图分析，更新相关工单状态
"""

import base64
import asyncio
import json
import tempfile
import os
from datetime import datetime
from typing import Dict, Any, Optional, List

import pytz
from pymongo import DESCENDING
from pydub import AudioSegment

from dao.es_dao.card_dao import get_doc_by_imei, get_card_list
from model.es.card import ElectronicCardModel
from utils.command_util import send_set_device_config
from .base_handler import BaseHandler
from client.openai_client import invoke_laozhangapi_only_result, invoke_openai_only_result
from model.wristband_models import AudioData
from utils.aliyun_token_util import send_asr
from utils.mongo_util import db


class AudioHandler(BaseHandler):
    """音频数据处理器"""

    # 意图识别结果常量
    INTENT_CODES = {
        'RECEIVED': '2',  # 收到
        'COMPLETED': '3',  # 完成
        'REJECTED': '0',  # 拒绝
        'UNKNOWN': '-1'  # 未知意图
    }

    # 工单状态常量
    ORDER_STATES = {
        'ASSIGNED': 2,  # 已分配
        'RECEIVED': 3,  # 已接收
        'IN_PROGRESS': 3,  # 进行中
        'COMPLETED': 4  # 已完成
    }

    # 音频增益级别常量
    GAIN_LEVELS = {
        'LOW': 3.0,      # 低增益：轻微放大
        'NORMAL': 6.0,   # 正常增益：标准放大
        'HIGH': 12.0,    # 高增益：较大放大
        'MAX': 20.0,     # 最大增益：最大放大（可能有失真）
        'EXTREME': 30.0  # 极限增益：极大放大（接受失真换取音量）
    }

    def __init__(self):
        super().__init__()
        self.beijing_tz = pytz.timezone('Asia/Shanghai')

    async def handle(self, device_id: str, data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """
        处理音频数据
        
        Args:
            device_id: 设备ID
            data: 音频数据字典
            
        Returns:
            处理结果，包含意图识别结果
        """
        try:
            # 将字典数据转换为AudioData模型
            audio = AudioData(**data)
            intent = await self._process_audio_data(audio)

            json = {

            }

            return {
                "status": "success",
                "device_id": device_id,
                "intent": intent,
                "processed": intent != self.INTENT_CODES['UNKNOWN']
            }

        except Exception as e:
            self.log_error(device_id, "音频", e)
            return None

    async def _process_audio_data(self, audio: AudioData) -> str:
        """
        处理音频数据的核心逻辑
        
        业务流程：
        1. 将Base64编码的音频转换为字节数据
        2. 调用语音识别服务（ASR）转换为文本
        3. 使用AI进行意图识别
        4. 根据识别结果更新工单状态
        
        Args:
            audio: 音频数据模型
            
        Returns:
            意图识别结果代码
        """
        try:
            # 步骤1: 解码音频数据
            audio_bytes = self._decode_audio_data(audio.audio)

            # 步骤2: 异步语音识别
            asr_text = await self._perform_speech_recognition(audio_bytes)
            if not asr_text:
                self.logger.warning(f"语音识别失败: {audio.device_id}")
                return self.INTENT_CODES['UNKNOWN']

            self.logger.info(f"语音识别结果: {audio.device_id} -> {asr_text}")

            # 步骤3: AI意图识别
            intent_code = await self._analyze_intent(asr_text, audio.device_id)

            # 步骤4: 识别是和谁说话
            # 在本项目的员工中查询是否有人名和本句中所说的人名一致。然后获取到他的IMEI

            name = intent_code.get('name')
            if name:
                card_list = await get_card_list({'nickName': name})
                print(card_list)
                
                # 放大音频音量并转换为BASE64 - 如果声音太小可以改为 'HIGH', 'MAX', 或 'EXTREME'
                amplified_audio_b64 = await self._amplify_audio_volume(audio.audio, gain_level='MAX')
                
                for card in card_list:
                    data = {
                        "url": "/sendMessageToDevice",
                        "body": {
                            "imei": card.get('imei'),
                            "content": amplified_audio_b64,
                            "type": 0
                        }
                    }
                    await send_set_device_config(data)
            # 步骤4: 根据意图更新工单状态
            if intent_code != self.INTENT_CODES['UNKNOWN']:
                await self._update_work_order_status(
                    audio.device_id, intent_code, asr_text, audio.timestamp
                )
                self.logger.info(f"音频处理完成: {audio.device_id}, 意图: {intent_code}")

            return intent_code

        except Exception as e:
            self.logger.error(f"音频处理异常: {audio.device_id}, 错误: {e}")
            return self.INTENT_CODES['UNKNOWN']

    def _decode_audio_data(self, voice_b64: str) -> bytes:
        """
        解码Base64编码的音频数据
        
        Args:
            voice_b64: Base64编码的音频数据
            
        Returns:
            音频字节数据
        """
        try:
            return base64.b64decode(voice_b64)
        except Exception as e:
            self.logger.error(f"音频数据解码失败: {e}")
            raise

    async def _perform_speech_recognition(self, audio_bytes: bytes) -> Optional[str]:
        """
        执行语音识别
        
        Args:
            audio_bytes: 音频字节数据
            
        Returns:
            识别出的文本，失败时返回None
        """
        try:
            # 使用线程池执行阻塞的ASR调用
            asr_result = await asyncio.to_thread(send_asr, audio_bytes)
            return asr_result

        except Exception as e:
            self.logger.error(f"语音识别调用失败: {e}")
            return None

    async def _analyze_intent(self, text: str, device_id: str) -> str:
        """
        分析语音文本的意图
        
        Args:
            text: 语音识别出的文本
            
        Returns:
            意图代码
        """
        try:
            # 构建意图识别提示词
            card: ElectronicCardModel = await get_doc_by_imei(device_id)
            print(card)

            card_list = await get_card_list({'depart': [card.depart]})
            print(card_list)
            # 调用AI进行意图识别
            prompt = self._build_intent_prompt(text, [card.get('nickName') for card in card_list])
            intent_result = await invoke_openai_only_result(prompt, model='deepseek-chat')

            # 清理可能的代码块标记并解析JSON
            cleaned_result = intent_result.strip()
            if cleaned_result.startswith('```json'):
                cleaned_result = cleaned_result[7:]  # 移除开头的```json
            if cleaned_result.endswith('```'):
                cleaned_result = cleaned_result[:-3]  # 移除结尾的```
            cleaned_result = cleaned_result.strip()

            # 验证并返回意图代码
            return json.loads(cleaned_result)

        except Exception as e:
            self.logger.error(f"意图识别失败: {e}")
            return self.INTENT_CODES['UNKNOWN']

    def _build_intent_prompt(self, text: str, target_name_list: List[dict]) -> str:
        """
        构建意图识别的提示词
        
        Args:
            text: 待分析的文本
            
        Returns:
            提示词字符串
        """
        return (
            f'我下面的话要判断他的意思，你需要输出一个JSON字符串,不要带任何代码注释，要纯JSON,不要带什么```json 这样的注释。切记'
            f'第一个字段是这段话的语义是否是接收工单，字段名称是 "receive"，输出一个整数字符'
            f'如果是表示收到了就输出{self.INTENT_CODES["RECEIVED"]}，'
            f'表示完成了就输出{self.INTENT_CODES["COMPLETED"]}，'
            f'如果表示拒绝就输出{self.INTENT_CODES["REJECTED"]}，'
            f'没有以上三种含义就输出{self.INTENT_CODES["UNKNOWN"]}'
            f'第二个字段是你是否从这段话中的说话对象这个字符串中匹配到了以下这个JSON字符串中几个人名中匹配到了可能得名字，如果仅仅能识别出这个人的姓，你可以把姓这个的都返回，比如李总，李经理，李姐等，{target_name_list}，因为是语音转换的内容可能，所以你需要判断一下谐音，'
            f'当出现无法理解的名称时要根据拼音谐音等进判断。返回字段是字符串数组，无论是多个还是一个都以字符串数组的方式返回，'
            f'字段名称是 "name"，如果没有匹配到则输出空数组'
            f'以下是我说的话 :{text}'
        )

    def _validate_intent_result(self, result: str) -> str:
        """
        验证意图识别结果
        
        Args:
            result: AI返回的结果
            
        Returns:
            验证后的意图代码
        """
        valid_codes = set(self.INTENT_CODES.values())
        return result if result in valid_codes else self.INTENT_CODES['UNKNOWN']

    async def _update_work_order_status(self, device_id: str, intent_code: str,
                                        message: str, timestamp: int) -> None:
        """
        根据意图更新工单状态
        
        Args:
            device_id: 设备ID
            intent_code: 意图代码
            message: 语音识别文本
            timestamp: 时间戳
        """
        try:
            current_time = datetime.fromtimestamp(timestamp, self.beijing_tz)

            if intent_code == self.INTENT_CODES['RECEIVED']:
                # 处理"收到"意图 - 更新为已接收状态
                await self._update_order_received(device_id, message, current_time)

            elif intent_code == self.INTENT_CODES['COMPLETED']:
                # 处理"完成"意图 - 更新为已完成状态
                await self._update_order_completed(device_id, message, current_time)

            elif intent_code == self.INTENT_CODES['REJECTED']:
                # 处理"拒绝"意图 - 可以记录拒绝状态或其他处理
                await self._update_order_rejected(device_id, message, current_time)

        except Exception as e:
            self.logger.error(f"更新工单状态失败: {device_id}, 错误: {e}")

    async def _update_order_received(self, device_id: str, message: str,
                                     current_time: datetime) -> None:
        """
        更新工单为已接收状态
        
        Args:
            device_id: 设备ID
            message: 确认消息
            current_time: 当前时间
        """
        filter_query = {
            "handler.imei": device_id,
            'state': self.ORDER_STATES['ASSIGNED']  # 查找已分配状态的工单
        }

        update_query = {
            "$set": {
                'state': self.ORDER_STATES['RECEIVED'],
                'handlingTime': current_time,
                'handlingMessage': message
            }
        }

        await self._async_mongo_update(filter_query, update_query)
        self.logger.info(f"工单已更新为接收状态: {device_id}")

    async def _update_order_completed(self, device_id: str, message: str,
                                      current_time: datetime) -> None:
        """
        更新工单为已完成状态
        
        Args:
            device_id: 设备ID
            message: 完成消息
            current_time: 当前时间
        """
        filter_query = {
            "handler.imei": device_id,
            'state': self.ORDER_STATES['IN_PROGRESS']  # 查找进行中状态的工单
        }

        update_query = {
            "$set": {
                'state': self.ORDER_STATES['COMPLETED'],
                'completionTime': current_time,
                'completionMessage': message
            }
        }

        await self._async_mongo_update(filter_query, update_query)
        self.logger.info(f"工单已更新为完成状态: {device_id}")

    async def _update_order_rejected(self, device_id: str, message: str,
                                     current_time: datetime) -> None:
        """
        处理工单拒绝状态
        
        Args:
            device_id: 设备ID
            message: 拒绝消息
            current_time: 当前时间
        """
        # 可以根据业务需求决定如何处理拒绝状态
        # 这里暂时只记录日志，实际业务可能需要更新特定状态或发送通知
        self.logger.warning(f"收到工单拒绝: {device_id}, 消息: {message}")

        # 如果需要记录拒绝状态，可以更新数据库
        # filter_query = {"handler.imei": device_id, 'state': {'$in': [2, 3]}}
        # update_query = {"$set": {'rejectedTime': current_time, 'rejectedMessage': message}}
        # await self._async_mongo_update(filter_query, update_query)

    async def _async_mongo_update(self, filter_doc: dict, update_doc: dict) -> None:
        """
        异步执行MongoDB更新操作
        
        Args:
            filter_doc: 查询条件
            update_doc: 更新内容
        """
        try:
            # 使用线程池执行同步MongoDB操作
            result = await asyncio.to_thread(
                db.s_clean_keeping_order.find_one_and_update,
                filter_doc,
                update_doc,
                sort=[("createDate", DESCENDING)]
            )

            if result:
                self.logger.debug(f"MongoDB更新成功: {filter_doc}")
            else:
                self.logger.warning(f"MongoDB更新未找到匹配文档: {filter_doc}")

        except Exception as e:
            self.logger.error(f"MongoDB更新操作失败: {e}")
            raise

    def _get_intent_description(self, intent_code: str) -> str:
        """
        获取意图代码的描述
        
        Args:
            intent_code: 意图代码
            
        Returns:
            意图描述
        """
        descriptions = {
            self.INTENT_CODES['RECEIVED']: '已接收',
            self.INTENT_CODES['COMPLETED']: '已完成',
            self.INTENT_CODES['REJECTED']: '已拒绝',
            self.INTENT_CODES['UNKNOWN']: '未知意图'
        }

        return descriptions.get(intent_code, f'未知代码({intent_code})')

    async def _amplify_audio_volume(self, audio_b64: str, gain_level: str = 'NORMAL', custom_gain_db: float = None) -> str:
        """
        放大BASE64格式AMR音频的音量并优化音质
        
        Args:
            audio_b64: BASE64编码的AMR音频数据
            gain_level: 增益级别 ('LOW', 'NORMAL', 'HIGH', 'MAX', 'EXTREME')
            custom_gain_db: 自定义增益值（分贝），如果提供则忽略gain_level
            
        Returns:
            放大后的BASE64编码AMR音频数据
        """
        try:
            # 确定增益值
            if custom_gain_db is not None:
                gain_db = custom_gain_db
                self.logger.info(f"使用自定义增益: {gain_db}dB")
            else:
                gain_db = self.GAIN_LEVELS.get(gain_level, self.GAIN_LEVELS['MAX'])
                self.logger.info(f"使用增益级别 {gain_level}: {gain_db}dB")
            
            # 解码BASE64音频数据
            audio_bytes = base64.b64decode(audio_b64)
            
            # 使用临时目录处理音频文件
            with tempfile.TemporaryDirectory() as tmpdir:
                input_amr_path = os.path.join(tmpdir, "input.amr")
                wav_path = os.path.join(tmpdir, "temp.wav")
                output_amr_path = os.path.join(tmpdir, "output.amr")
                
                # 将音频字节写入临时AMR文件
                with open(input_amr_path, "wb") as f:
                    f.write(audio_bytes)
                
                # 加载AMR文件
                audio_segment = AudioSegment.from_file(input_amr_path, format="amr")
                
                # 音频质量优化处理
                # 1. 先转换为WAV格式进行处理（更好的音质）
                audio_segment = audio_segment.set_frame_rate(8000).set_channels(1)
                
                # 2. 根据增益级别采用不同的处理策略
                if gain_level in ['MAX', 'EXTREME'] or (custom_gain_db and custom_gain_db > 15):
                    # 高增益模式：接受一定失真以获得更大音量
                    self.logger.warning(f"使用高增益模式，可能产生失真")
                    amplified_audio = audio_segment + gain_db
                    # 对于极高增益，使用激进的标准化
                    if gain_level == 'EXTREME' or (custom_gain_db and custom_gain_db > 25):
                        amplified_audio = amplified_audio.normalize(headroom=0.1)
                    else:
                        amplified_audio = amplified_audio.normalize(headroom=1.0)
                else:
                    # 普通模式：保护音质，避免削波失真
                    max_possible_gain = audio_segment.max_possible_amplitude() - audio_segment.max
                    if max_possible_gain > 0:
                        # 如果有空间可以放大，限制增益避免削波
                        safe_gain = min(gain_db, max_possible_gain * 0.1)  # 保守的增益
                    else:
                        # 如果已经接近最大值，使用音频压缩代替简单放大
                        safe_gain = min(gain_db, 5.0)  # 限制最大增益
                    
                    # 3. 应用增益
                    amplified_audio = audio_segment + safe_gain
                    
                    # 4. 应用音频标准化，确保音量一致且避免失真
                    amplified_audio = amplified_audio.normalize()
                    
                    # 5. 应用轻微的音频压缩，减少动态范围，提高清晰度
                    amplified_audio = amplified_audio.compress_dynamic_range(threshold=-20.0, ratio=2.0)
                
                # 6. 导出为高质量WAV再转AMR，保持更好音质
                amplified_audio.export(wav_path, format="wav")
                
                # 7. 重新加载WAV并导出为AMR，使用更好的编码参数
                final_audio = AudioSegment.from_wav(wav_path)
                
                # 根据增益级别调整输出参数
                export_params = [
                    "-ar", "8000",      # 采样率
                    "-ac", "1",         # 单声道
                    "-ab", "12.2k",     # 比特率
                    "-acodec", "amr_nb"  # 使用AMR-NB编码器
                ]
                
                # 只有在非极高增益模式下才应用滤波器
                if gain_level not in ['EXTREME'] and not (custom_gain_db and custom_gain_db > 25):
                    export_params.extend(["-af", "highpass=f=80,lowpass=f=3400"])
                
                final_audio.export(output_amr_path, format="amr", parameters=export_params)
                
                # 读取处理后的AMR文件并转换为BASE64
                with open(output_amr_path, "rb") as f:
                    amplified_bytes = f.read()
                
                return base64.b64encode(amplified_bytes).decode()
                
        except Exception as e:
            self.logger.error(f"音频放大失败: {e}")
            # 如果放大失败，返回原始音频
            return audio_b64

    async def get_audio_processing_statistics(self, device_id: str,
                                              start_time: int, end_time: int) -> Dict[str, int]:
        """
        获取音频处理统计信息
        
        Args:
            device_id: 设备ID
            start_time: 开始时间戳
            end_time: 结束时间戳
            
        Returns:
            音频处理统计字典
        """
        # TODO: 实现音频处理统计逻辑
        return {
            'total_audio': 0,
            'received_count': 0,
            'completed_count': 0,
            'rejected_count': 0,
            'unknown_count': 0
        }
